<?php

namespace app\service\modbus;

use EchoLogger;
use Exception;
use ModbusTcpClient\Network\BinaryStreamConnection;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersResponse;
use ModbusTcpClient\Packet\RtuConverter;
use ModbusTcpClient\Utils\Packet;
use support\Response;

class ModbusRtuService
{

    /**
     * 获取串口的信息
     * @param string $device
     * @param int $startAddress
     * @param int $quantity
     * @param int $slaveId
     * @return int[]|Response
     */
    public function getSerial(string $device="COM1", int $startAddress = 1, int $quantity = 2, int $slaveId = 1)
    {
        $fd = fopen($device, 'w+b');
        $sttyModes = implode(' ', [
            'cs8', // enable character size 8 bits
            '9600', // enable baud rate 9600
            '-cstopb', // 1 stop bit
            '-parenb', // parity none

            '-icanon', // disable enable special characters: erase, kill, werase, rprnt
            'min 0', // with -icanon, set N characters minimum for a completed read
            'ignbrk', // enable ignore break characters
            '-brkint', // disable breaks cause an interrupt signal
            '-icrnl', // disable translate carriage return to newline
            '-imaxbel', // disable beep and do not flush a full input buffer on a character
            '-opost', // disable postprocess output
            '-onlcr', // disable translate newline to carriage return-newline
            '-isig', // disable interrupt, quit, and suspend special characters
            '-iexten', // disable non-POSIX special characters
            '-echo', // disable echo input characters
            '-echoe', // disable echo erase characters as backspace-space-backspace
            '-echok', // disable echo a newline after a kill character
            '-echoctl', // disable same as [-]ctlecho
            '-echoke', // disable kill all line by obeying the echoprt and echoe settings
            '-noflsh', // disable flushing after interrupt and quit special characters
            '-ixon', // disable XON/XOFF flow control
            '-crtscts', // disable RTS/CTS handshaking
        ]);
        $sttyResult = exec("stty -F {$device} {$sttyModes}");
        if (!$sttyResult) {
           return  errorMessage("stty command failed");
        }
        $connection = BinaryStreamConnection::getBuilder()
            ->setUri($device)
            ->setProtocol('serial')
            ->setIsCompleteCallback(static function ($binaryData, $streamIndex): bool {
                return Packet::isCompleteLengthRTU($binaryData);
            })
            ->setDelayRead(100) // 100 milliseconds, serial devices may need delay between sending and received
            ->setLogger(new EchoLogger())
            ->build();
        $tcpPacket = new ReadInputRegistersRequest($startAddress, $quantity, $slaveId);
        $rtuPacket = RtuConverter::toRtu($tcpPacket);
        fwrite($fd, $rtuPacket);
        fflush($fd);

        try {
            $binaryData = $connection->connect()->sendAndReceive($rtuPacket);
            /** @var ReadInputRegistersResponse $response */
            $response = RtuConverter::fromRtu($binaryData)->withStartAddress($startAddress);
            return $response->getData();
        } catch (Exception $exception) {
            return errorMessage($exception->getMessage());
        } finally {
            $connection->close();
        }
    }

}